package jwt

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

type generator struct {
	secret    string
	algorithm IAlgorithm
	header    IHeader
	payload   IPayload
}

func NewGenerator(secret string, algorithm IAlgorithm) *generator {
	return &generator{
		secret:    secret,
		algorithm: algorithm,
	}
}

func (g *generator) SetSecret(secret string) *generator {
	g.secret = secret

	return g
}

func (g *generator) SetAlgorithm(algorithm IAlgorithm) *generator {
	g.algorithm = algorithm

	return g
}

func (g *generator) SetHeader(header IHeader) *generator {
	g.header = header

	return g
}

func (g *generator) SetPayload(payload IPayload) *generator {
	g.payload = payload

	return g
}

func (g *generator) Token() (token string, err error) {
	if nil == g.header {
		err = errors.New("header is nil")
		return
	}

	if nil == g.payload {
		err = errors.New("payload is nil")
		return
	}

	var headerBuf []byte
	if headerBuf, err = json.Marshal(g.header); nil != err {
		return
	}

	var payloadBuf []byte
	if payloadBuf, err = json.Marshal(g.payload); nil != err {
		return
	}

	headerStr := base64.RawURLEncoding.EncodeToString(headerBuf)
	payloadStr := base64.RawURLEncoding.EncodeToString(payloadBuf)
	unSign := fmt.Sprintf("%s.%s", headerStr, payloadStr)

	token, err = g.TokenByString(unSign)

	return
}

func (g *generator) TokenByString(data string) (token string, err error) {
	if "" == data || 1 != strings.Count(data, ".") {
		err = errors.New("invalid data string")
		return
	}

	if nil == g.algorithm {
		err = errors.New("algorithm is nil")
		return
	}

	var sign string
	if sign, err = g.algorithm.Sign(data, g.secret); nil != err {
		return
	}

	token = fmt.Sprintf("%s.%s", data, sign)

	return
}
